package org.dromara.tearoom.util;

import org.dromara.tearoom.domain.TerOrder;
import org.dromara.tearoom.domain.vo.TimeSlot;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


public class TimeShiftUtil {
    //计算空闲时间段
    public static List<Map<String, Integer>> findFreeTimeSlotsByDay(
            List<TerOrder> occupiedSlots,
            LocalDateTime start,
            LocalDateTime end) {

        List<Map<String, Integer>> result = new ArrayList<>();

        // 计算处理日期范围
        LocalDateTime currentDate = start.toLocalDate().atStartOfDay();
        LocalDateTime endDateLimit = end.toLocalDate().plusDays(1).atStartOfDay();

        while (currentDate.isBefore(endDateLimit)) {
            LocalDateTime dayStart;
            LocalDateTime dayEnd;

            // ==== 处理当前天的有效开始时间 ====
            if (currentDate.toLocalDate().isEqual(start.toLocalDate())) {
                // 第一天：以开始时间向下取整到半小时起点
                dayStart = roundDownToExactHalfHour(start);
            } else {
                // 非第一天：从当日00:00开始
                dayStart = currentDate;
            }

            // ==== 处理当前天的有效结束时间 ====
            if (currentDate.toLocalDate().isEqual(end.toLocalDate())) {
                dayEnd = end;
            } else {
                dayEnd = currentDate.toLocalDate().atTime(23, 59, 59, 999999999);
            }

            // 跳过无效时间段
            if (dayStart.isAfter(dayEnd)) {
                currentDate = currentDate.plusDays(1);
                continue;
            }

            // 生成当天时间表
            List<TimeSlot> mergedSlots = getMergedSlots(occupiedSlots, dayStart, dayEnd);
            Map<String, Integer> dayMap = generateDayMap(mergedSlots, dayStart, dayEnd);
            if (!dayMap.isEmpty()) {
                result.add(dayMap);
            }

            currentDate = currentDate.plusDays(1);
        }

        return result;
    }

    /// 辅助方法：获取合并后的当天时间段
    private static List<TimeSlot> getMergedSlots(
            List<TerOrder> occupiedSlots,
            LocalDateTime dayStart, // 当前天的有效开始时间
            LocalDateTime dayEnd) {   // 当前天的有效结束时间

        List<TimeSlot> daySlots = new ArrayList<>();
        if (occupiedSlots != null) {
            for (TerOrder entity : occupiedSlots) {
                LocalDateTime eStart = parseDateTime(entity.getStartTime());
                LocalDateTime eEnd = parseDateTime(entity.getEndTime());

                // ** 核心修改：将每个订单的结束时间延长半小时 **
                LocalDateTime extendedEnd = eEnd.plusMinutes(30);

                // ** 筛选与当前天的【有效范围 [dayStart, dayEnd]】重叠的时间段 **
                if (extendedEnd.isAfter(dayStart) && eStart.isBefore(dayEnd)) {

                    // ** 将重叠部分裁剪到当前天的【有效范围 [dayStart, dayEnd]】内 **
                    LocalDateTime newStart = eStart.isBefore(dayStart) ? dayStart : eStart;
                    LocalDateTime newEnd = extendedEnd.isAfter(dayEnd) ? dayEnd : extendedEnd;

                    // 只有裁剪后仍然是有效的时间段 [start, end) 才添加
                    if(newStart.isBefore(newEnd)){
                        daySlots.add(new TimeSlot(newStart, newEnd));
                    }
                }
            }
        }
        return mergeOverlapping(daySlots);
    }
    // 生成时间表并标记后续半小时
    private static Map<String, Integer> generateDayMap(
            List<TimeSlot> occupiedSlots,
            LocalDateTime dayStart,
            LocalDateTime dayEnd) {

        Map<String, Integer> map = new HashMap<>();
        LocalDateTime currentSlotStart = dayStart;
        LocalDateTime dayEndLimit = roundUpToHalfHour(dayEnd);

        while (currentSlotStart.isBefore(dayEndLimit)) {
            // 关键修复：将当前时间点复制给 final 变量
            final LocalDateTime currentSlot = currentSlotStart;

            String key = currentSlot.format(DateTimeFormatter.ofPattern("HH:mm"));
            boolean isOccupied = occupiedSlots.stream()
                    .anyMatch(slot ->
                            // 使用 final 变量 currentSlot 替代 currentSlotStart
                            !currentSlot.isBefore(slot.start()) &&
                                    currentSlot.isBefore(slot.end())
                    );
            map.put(key, isOccupied ? 1 : 0);

            currentSlotStart = currentSlotStart.plusMinutes(30); // 更新循环变量
        }

        return map;
    }

    //合并时间段
    private static List<TimeSlot> mergeOverlapping(List<TimeSlot> slots) {
        if (slots == null || slots.isEmpty()) return new ArrayList<>();
        // 按开始时间排序
        // 使用 record 的 start() 访问器
        slots.sort(Comparator.comparing(TimeSlot::start));

        List<TimeSlot> merged = new ArrayList<>();
        TimeSlot current = slots.get(0); // 使用 record

        for (int i = 1; i < slots.size(); i++) {
            TimeSlot next = slots.get(i); // 使用 record
            // 如果下一个时间段的开始时间早于或等于当前时间段的结束时间，说明重叠或相邻
            // 使用 record 的 start() 和 end() 访问器
            if (!next.start().isAfter(current.end())) {
                // 合并，取当前开始时间和两个结束时间中较晚的一个
                // 使用 record 的 start() 和 end() 访问器创建新的 record 实例
                current = new TimeSlot(
                        current.start(),
                        current.end().isAfter(next.end()) ? current.end() : next.end()
                );
            } else {
                // 不重叠，将当前时间段加入结果集，并更新当前时间段为下一个
                merged.add(current);
                current = next;
            }
        }
        // 添加最后一个时间段
        merged.add(current);
        return merged;
    }


    private static LocalDateTime parseDateTime(String dateTimeStr) {
        return LocalDateTime.parse(dateTimeStr,
                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    // 新增向上取整到半小时点的方法
    private static LocalDateTime roundUpToHalfHour(LocalDateTime time) {
        int minute = time.getMinute();
        int second = time.getSecond();
        int nano = time.getNano();

        // 清零秒和纳秒，只考虑分钟
        LocalDateTime truncatedTime = time.withSecond(0).withNano(0);

        if (minute == 0 && second == 0 && nano == 0) {
            // 恰好在 xx:00.000000000 或 xx:30.000000000，返回原时间
            return time;
        } else if (minute < 30) {
            // 在 xx:01 到 xx:29 之间 (或 xx:00 有秒/纳秒)，向上取整到 xx:30
            return truncatedTime.withMinute(30);
        } else {
            // 在 xx:31 到 xx:59 之间 (或 xx:30 有秒/纳秒)，向上取整到 (xx+1):00
            return truncatedTime.plusHours(1).withMinute(0);
        }
    }
    private static LocalDateTime roundDownToExactHalfHour(LocalDateTime time) {
        return time.withMinute((time.getMinute() >= 30) ? 30 : 0)
                .withSecond(0)
                .withNano(0);
    }
    public static void main(String[] args) {
        List<TerOrder> occupied = new ArrayList<>();
        occupied.add(new TerOrder("2025-05-19 09:30:00", "2025-05-19 11:30:00"));
        occupied.add(new TerOrder("2025-05-19 18:00:00", "2025-05-19 19:00:00"));
        occupied.add(new TerOrder("2025-05-19 23:00:00", "2025-05-20 01:00:00"));

        LocalDateTime start = LocalDateTime.of(2025, 05, 19, 9, 31);
        LocalDateTime end = LocalDateTime.of(2025, 05, 21, 23, 30);

        List<Map<String, Integer>> result = findFreeTimeSlotsByDay(occupied, start, end);

        for (int i = 0; i < result.size(); i++) {
            LocalDateTime currentDate = start.toLocalDate().atStartOfDay().plusDays(i);
            System.out.println("Day " + (i + 1) + ":");
            Map<String, Integer> dayMap = result.get(i);

            // 修正排序逻辑，使用完整的日期时间字符串
            List<String> sortedKeys = dayMap.keySet().stream()
                    .sorted(Comparator.comparing(key -> {
                        String fullDateTime = currentDate.format(DateTimeFormatter.ISO_LOCAL_DATE) + " " + key + ":00";
                        return LocalDateTime.parse(fullDateTime,
                                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    }))
                    .collect(Collectors.toList());

            for (String key : sortedKeys) {
                System.out.println(key + " -> " + dayMap.get(key));
            }
            System.out.println();
        }
    }


}
